(function($) {
    $.fn.flickr = function(o) {
        var s = {
            api_key: null,              // [string]    required, see http://www.flickr.com/services/api/misc.api_keys.html
            type: null,                 // [string]    allowed values: 'photoset', 'search', default: 'flickr.photos.getRecent'
            photoset_id: null,          // [string]    required, for type=='photoset'
            text: null,                        // [string]    for type=='search' free text search
            user_id: null,              // [string]    for type=='search' search by user id
            group_id: null,             // [string]    for type=='search' search by group id
            tags: null,                 // [string]    for type=='search' comma separated list
            tag_mode: 'any',            // [string]    for type=='search' allowed values: 'any' (OR), 'all' (AND)
            sort: 'relevance',    // [string]    for type=='search' allowed values: 'date-posted-asc', 'date-posted-desc', 'date-taken-asc', 'date-taken-desc', 'interestingness-desc', 'interestingness-asc', 'relevance'
            thumb_size: 'm',            // [string]    allowed values: 's' (75x75), 't' (100x?), 'm' (240x?)
            size: null,                 // [string]    allowed values: 'm' (240x?), 'b' (1024x?), 'o' (original), default: (500x?)
            per_page: 100,              // [integer]   allowed values: max of 500
            page: 1,                       // [integer]   see paging notes
            attr: '',                   // [string]    optional, attributes applied to thumbnail <a> tag
            api_url: null,              // [string]    optional, custom url that returns flickr JSON or JSON-P 'photos' or 'photoset'
            params: '',                 // [string]    optional, custom arguments, see http://www.flickr.com/services/api/flickr.photos.search.html
            api_callback: '?',          // [string]    optional, custom callback in flickr JSON-P response
            callback: null,             // [function]  optional, callback function applied to entire <ul>
            label: null                 // [string]    optional, gallery label

            // SEARCH NOTES: when setting type to 'search' at least one search parameter  must also be passed text, user_id, group_id, or tags

            // SIZE NOTES: photos must allow viewing original size for size 'o' to function, if not, default size is shown
        };
        if (o) $.extend(s, o);

        s.type = 'photoset';
        if (null != s.userId) {
            s.type = 'publicPhotos';
        }

        return this.each(function() {
            // create unordered list to contain flickr images
            var url = $.flickr.format(s);
            $.getJSON(url, function(r) {
                Photto.renderThumbnails(r, s);
            });
        });
    };

// static function to format the flickr API url according to the plug-in settings
    $.flickr = {
        format: function(s) {
            if (s.url) return s.url;
            var url = 'https://api.flickr.com/services/rest/?format=json&jsoncallback=' + s.api_callback + '&api_key=' + s.api_key;
            switch (s.type) {
                case 'photoset':
                    url += '&method=flickr.photosets.getPhotos&photoset_id=' + s.photoset_id;
                    break;
                case 'publicPhotos':
                    url += '&method=flickr.people.getPublicPhotos&photoset_id=' + s.photoset_id + '&user_id=' + s.user_id;
                    break;
                case 'search':
                    url += '&method=flickr.photos.search&sort=' + s.sort;
                    if (s.user_id) url += '&user_id=' + s.user_id;
                    if (s.group_id) url += '&group_id=' + s.group_id;
                    if (s.tags) url += '&tags=' + s.tags;
                    if (s.tag_mode) url += '&tag_mode=' + s.tag_mode;
                    if (s.text) url += '&text=' + s.text;
                    break;
                default:
                    url += '&method=flickr.photos.getRecent';
            }
            if (s.size == 'o') url += '&extras=original_format';
            url += '&per_page=' + s.per_page + '&page=' + s.page + s.params;
            return url;
        }
    };
})(jQuery);

var Photto = function() {
};

Photto.flickrSets = [];

Photto.renderThumbnails = function(r, s) {
    var list = $('#content');
    var galleriesContent = $('#galleriesContent');

    if (r.stat != "ok") {
        for (i in r) {
            $('<li>').text(i + ': ' + r[i]).appendTo(list);
        }
    } else {
        if (s.type == 'photoset') r.photos = r.photoset;

        var galleryIndex = Photto.flickrSets.length;
        Photto.flickrSets.push(r.photos);

        var photo = r.photos.photo[0];
        // format thumbnail url
        var t = 'http://farm' + photo['farm'] + '.static.flickr.com/' + photo['server'] + '/' + photo['id'] + '_' + photo['secret'] + '_' + s.thumb_size + '.jpg';
        //format image url
        var h = 'http://farm' + photo['farm'] + '.static.flickr.com/' + photo['server'] + '/' + photo['id'] + '_';
        var hm = 'http://farm' + photo['farm'] + '.static.flickr.com/' + photo['server'] + '/' + photo['id'] + '_' + photo['secret'] + '_m.jpg';
        var hmed = 'http://farm' + photo['farm'] + '.static.flickr.com/' + photo['server'] + '/' + photo['id'] + '_' + photo['secret'] + '.jpg';
        var hb = 'http://farm' + photo['farm'] + '.static.flickr.com/' + photo['server'] + '/' + photo['id'] + '_' + photo['secret'] + '_b.jpg';
        switch (s.size) {
            case 'm':
                h += photo['secret'] + '_m.jpg';
                break;
            case 'b':
                h += photo['secret'] + '_b.jpg';
                break;
            case 'o':
                if (photo['originalsecret'] && photo['originalformat']) {
                    h += photo['originalsecret'] + '_o.' + photo['originalformat'];
                } else {
                    h += photo['secret'] + '_b.jpg';
                }
                break;
            default:
                h += photo['secret'] + '.jpg';
        }
        var flickrLink = r.photoset ? 'http://www.flickr.com/photos/' + r.photoset.owner + '/' + photo['id'] : '#';
        var imgContainer = document.createElement("div");
        imgContainer.className = "imageContainer";
        var img = document.createElement("img");
        img.src = h;
        img.alt = hb;

        if (null != s.label) {
            var label = document.createElement("p");
            label.innerHTML = s.label;
            imgContainer.appendChild(label);
        }

        imgContainer.appendChild(img);
        galleriesContent.append(imgContainer);

        $(imgContainer).bind('click', function() {
            Photto.showGalleryCallback(galleryIndex);
        });

        if (s.callback) s.callback(list);
    }
};

Photto.renderSmallThumbnails = function(galleryIndex) {
    var list = $('#content');
    $(list).empty();
    var size = 's';
    var thumb_size = 'm';
    var photos = Photto.flickrSets[galleryIndex];
    if (null == photos) {
        window.setTimeout(function() {
            Photto.renderSmallThumbnails(galleryIndex);
        }, 1);
        return;
    }
    for (var i = 0; i < photos.photo.length; i++) {
        var photo = photos.photo[i];
        // format thumbnail url
        var t = 'http://farm' + photo['farm'] + '.static.flickr.com/' + photo['server'] + '/' + photo['id'] + '_' + photo['secret'] + '_' + thumb_size + '.jpg';
        //format image url
        var h = 'http://farm' + photo['farm'] + '.static.flickr.com/' + photo['server'] + '/' + photo['id'] + '_';
        var hm = 'http://farm' + photo['farm'] + '.static.flickr.com/' + photo['server'] + '/' + photo['id'] + '_' + photo['secret'] + '_m.jpg';
        var hmed = 'http://farm' + photo['farm'] + '.static.flickr.com/' + photo['server'] + '/' + photo['id'] + '_' + photo['secret'] + '.jpg';
        var hb = 'http://farm' + photo['farm'] + '.static.flickr.com/' + photo['server'] + '/' + photo['id'] + '_' + photo['secret'] + '_b.jpg';
        switch (size) {
            case 'm':
                h += photo['secret'] + '_m.jpg';
                break;
            case 's':
                h += photo['secret'] + '_s.jpg';
                break;
            case 'b':
                h += photo['secret'] + '_b.jpg';
                break;
            case 'o':
                if (photo['originalsecret'] && photo['originalformat']) {
                    h += photo['originalsecret'] + '_o.' + photo['originalformat'];
                } else {
                    h += photo['secret'] + '_b.jpg';
                }
                break;
            default:
                h += photo['secret'] + '.jpg';
        }
        var flickrLink = photos.photoset ? 'http://www.flickr.com/photos/' + photos.photoset.owner + '/' + photo['id'] : '#';
        list.append('<img title="" src="' + h + '" alt="' + hb + '"' + (i > 0 ? '' : ' style="clear:both" ') + ' />');
    }

    Photto.makeGallery();
}

Photto.showGalleryCallback = Photto.renderSmallThumbnails;

Photto.makeGallery = function() {
    /* this is the index of the last clicked picture */
    var current = -1;
    /* number of pictures */
    var totalpictures = $('#content img').size();
    /* speed to animate the panel and the thumbs wrapper */
    var speed = 500;

    /* show the content */
    $('#content').show();

    /*
     when the user resizes the browser window,
     the size of the picture being viewed is recalculated;
     */
    $(window).bind('resize', function() {
        var $picture = $('#wrapper').find('img');
        resize($picture);
    });

    /*
     when hovering a thumb, animate it's opacity
     for a cool effect;
     when clicking on it, we load the corresponding large image;
     the source of the large image is stored as
     the "alt" attribute of the thumb image
     */
    $('#content > img').hover(
        function () {
            var $this = $(this);
            $this.stop().animate({'opacity':'1.0'}, 200);
        },
        function () {
            var $this = $(this);
            $this.stop().animate({'opacity':'0.8'}, 200);
        }).bind('click', function() {
            var $this = $(this);

            /* shows the loading icon */
            $('#loading').show();

            $('<img/>').load(
                function() {
                    $('#loading').hide();

                    if ($('#wrapper').find('img').length) return;
                    current = $this.index();
                    var $theImage = $(this);
                    /*
                     After it's loaded we hide the loading icon
                     and resize the image, given the window size;
                     then we append the image to the wrapper
                     */

                    resize($theImage);

                    $('#wrapper').append($theImage);
                    /* make its opacity animate */
                    $theImage.fadeIn(800);

                    /* and finally slide up the panel */
                    $('#panel').animate({'height':'100%'}, speed, function() {
                        /*
                         if the picture has a description,
                         it's stored in the title attribute of the thumb;
                         show it if it's not empty
                         */
                        var title = $this.attr('title');
                        if (title != '')
                            $('#description').html(title).show();
                        else
                            $('#description').empty().hide();

                        /*
                         if our picture is the first one,
                         don't show the "previous button"
                         for the slideshow navigation;
                         if our picture is the last one,
                         don't show the "next button"
                         for the slideshow navigation
                         */
                        if (current == 0)
                            $('#prev').hide();
                        else
                            $('#prev').fadeIn();
                        if (current == parseInt(totalpictures - 1))
                            $('#next').hide();
                        else
                            $('#next').fadeIn();
                        /*
                         we set the z-index and height of the thumbs wrapper
                         to 0, because we want to slide it up afterwards,
                         when the user clicks the large image
                         */
                        $('#thumbsWrapper').css({'z-index':'0','height':'0px'});
                        $('.infobar').show();
                    });
                }).attr('src', $this.attr('alt'));
        });

    /*
     when hovering a large image,
     we want to slide up the thumbs wrapper again,
     and reset the panel (like it was initially);
     this includes removing the large image element
     */
    $('#wrapper > img').live('click', function() {
        $this = $(this);
        $('#description').empty().hide();

        $('#thumbsWrapper').css('z-index', '10')
            .stop()
            .animate({'height':'100%'}, speed, function() {
                var $theWrapper = $(this);
                $('#panel').css('height', '0px');
                $theWrapper.css('z-index', '0');
                /*
                 remove the large image element
                 and the navigation buttons
                 */
                $this.remove();
                $('#prev').hide();
                $('#next').hide();
            });
        $('.infobar').hide();
    });

    /*
     when we are viewing a large image,
     if we navigate to the right/left we need to know
     which image is the corresponding neighbour.
     we know the index of the current picture (current),
     so we can easily get to the neighbour:
     */
    $('#next').unbind('click');
    $('#next').bind('click', function() {
        var $this = $(this);
        var $nextimage = $('#content img:nth-child(' + parseInt(current + 2) + ')');
        navigate($nextimage, 'right');
    });
    $('#prev').unbind('click');
    $('#prev').bind('click', function() {
        var $this = $(this);
        var $previmage = $('#content img:nth-child(' + parseInt(current) + ')');
        navigate($previmage, 'left');
    });

    document.onkeydown = function (event) {
        // handle cursor keys
        if (event.keyCode == 37) {
            // slide left
            $('#prev').click();
        } else if (event.keyCode == 39 || event.keyCode == 32) {
            // slide right
            $('#next').click();
        } else if (event.keyCode == 27) {
            // hide
            $('#wrapper > img').click();
        }
};

    /*
     given the next or previous image to show,
     and the direction, it loads a new image in the panel.
     */
    function navigate($nextimage, dir) {
        /*
         if we are at the end/beginning
         then there's no next/previous
         */
        if (dir == 'left' && current == 0)
            return;
        if (dir == 'right' && current == parseInt(totalpictures - 1))
            return;
        $('#loading').show();
        $('<img/>').load(
            function() {
                var $theImage = $(this);
                $('#loading').hide();
                $('#description').empty().fadeOut();

                $('#wrapper img').stop().fadeOut(500, function() {
                    var $this = $(this);

                    $this.remove();
                    resize($theImage);

                    $('#wrapper').append($theImage.show());
                    $theImage.stop().fadeIn(800);

                    var title = $nextimage.attr('title');
                    if (title != '') {
                        $('#description').html(title).show();
                    }
                    else
                        $('#description').empty().hide();

                    if (current == 0)
                        $('#prev').hide();
                    else
                        $('#prev').show();
                    if (current == parseInt(totalpictures - 1))
                        $('#next').hide();
                    else
                        $('#next').show();
                });
                /*
                 increase or decrease the current variable
                 */
                if (dir == 'right')
                    ++current;
                else if (dir == 'left')
                    --current;
            }).attr('src', $nextimage.attr('alt'));
    }

    /*
     resizes an image given the window size,
     considering the margin values
     */
    function resize($image) {
        var windowH = $(window).height() - 100;
        var windowW = $(window).width() - 80;
        var theImage = new Image();
        theImage.src = $image.attr("src");
        var imgwidth = theImage.width;
        var imgheight = theImage.height;

        if ((imgwidth > windowW) || (imgheight > windowH)) {
            if (imgwidth > imgheight) {
                var newwidth = windowW;
                var ratio = imgwidth / windowW;
                var newheight = imgheight / ratio;
                theImage.height = newheight;
                theImage.width = newwidth;
                if (newheight > windowH) {
                    var newnewheight = windowH;
                    var newratio = newheight / windowH;
                    var newnewwidth = newwidth / newratio;
                    theImage.width = newnewwidth;
                    theImage.height = newnewheight;
                }
            }
            else {
                var newheight = windowH;
                var ratio = imgheight / windowH;
                var newwidth = imgwidth / ratio;
                theImage.height = newheight;
                theImage.width = newwidth;
                if (newwidth > windowW) {
                    var newnewwidth = windowW;
                    var newratio = newwidth / windowW;
                    var newnewheight = newheight / newratio;
                    theImage.height = newnewheight;
                    theImage.width = newnewwidth;
                }
            }
        }
        $image.css({'width':theImage.width + 'px','height':theImage.height + 'px'});

        // center the image vertically
        var containerHeight = $("#panel").height();
        if (0 == containerHeight) {
            containerHeight = window.innerHeight;
        }
        var containerTopMargin = 40;
        var marginTop = (containerHeight - $image.height()) / 2 - containerTopMargin;
        if (marginTop > 0) {
            $image.css({'margin-top':marginTop});
        }
    }
};
